package com.sakura.oj.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sakura.oj.common.ErrorCode;
import com.sakura.oj.exception.BusinessException;
import com.sakura.oj.exception.ThrowUtils;
import com.sakura.oj.mapper.ProblemMapper;
import com.sakura.oj.model.dto.judge.JudgeCaseAddRequest;
import com.sakura.oj.model.dto.problem.ProblemQueryRequest;
import com.sakura.oj.model.entity.*;
import com.sakura.oj.model.enums.ProblemStatusEnum;
import com.sakura.oj.model.vo.HotProblemVo;
import com.sakura.oj.model.vo.ProblemVO;
import com.sakura.oj.service.ProblemJudgeCaseService;
import com.sakura.oj.service.ProblemService;
import com.sakura.oj.service.ProblemTagService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 题目服务实现
 */
@Service
@Slf4j
public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> implements ProblemService {

    @Resource
    private ProblemTagService problemTagService;

    @Resource
    private ProblemJudgeCaseService judgeCaseService;

    /**
     * 校验数据
     *
     * @param problem
     * @param add     对创建的数据进行校验
     */
    @Override
    public void validProblem(Problem problem, boolean add) {
        ThrowUtils.throwIf(problem == null, ErrorCode.PARAMS_ERROR);

        String title = problem.getTitle();
        // 创建数据时，参数不能为空
        if (add) {

            ThrowUtils.throwIf(StringUtils.isBlank(title), ErrorCode.PARAMS_ERROR);
        }
        // 修改数据时，有参数则校验

        if (StringUtils.isNotBlank(title)) {
            ThrowUtils.throwIf(title.length() > 80, ErrorCode.PARAMS_ERROR, "标题过长");
        }
    }

    /**
     * 获取查询条件
     *
     * @param problemQueryRequest
     * @return
     */
    @Override
    public QueryWrapper<Problem> getQueryWrapper(ProblemQueryRequest problemQueryRequest) {
        QueryWrapper<Problem> queryWrapper = new QueryWrapper<>();
        if (problemQueryRequest == null) {
            return queryWrapper;
        }
        Long id = problemQueryRequest.getId();
        String title = problemQueryRequest.getTitle();
        String content = problemQueryRequest.getContent();
        String searchText = problemQueryRequest.getSearchText();

        // 从多字段中搜索
        if (StringUtils.isNotBlank(searchText)) {
            // 需要拼接查询条件
            queryWrapper.and(qw -> qw.like("title", searchText).or().like("content", searchText));
        }
        // 模糊查询
        queryWrapper.like(StringUtils.isNotBlank(title), "title", title);
        queryWrapper.like(StringUtils.isNotBlank(content), "content", content);

        // 精确查询
        queryWrapper.eq(ObjectUtils.isNotEmpty(id), "id", id);

        return queryWrapper;
    }

    /**
     * 获取题目封装
     *
     * @param problem
     * @return
     */
    @Override
    public ProblemVO getProblemVO(Problem problem) {

        problem = getById(problem.getId());
        ThrowUtils.throwIf(problem == null, ErrorCode.NOT_FOUND_ERROR);

        // 对象转封装类
        ProblemVO problemVO = ProblemVO.objToVo(problem);

        List<Tag> tags = problemTagService.listByProblemId(problemVO.getId());
        problemVO.setTagsList(tags);

        String judgeConfig = problem.getJudgeConfig();
        problemVO.setJudgeConfig(JSONUtil.toBean(judgeConfig, JudgeConfig.class));

        return problemVO;
    }

    /**
     * 分页获取题目封装
     *
     * @return
     */
    @Override
    public Page<ProblemVO> getProblemVOPage(Page<Problem> problemPage, HttpServletRequest request) {
        List<Problem> problemList = problemPage.getRecords();
        Page<ProblemVO> problemVOPage = new Page<>(problemPage.getCurrent(), problemPage.getSize(), problemPage.getTotal());
        if (CollUtil.isEmpty(problemList)) {
            return problemVOPage;
        }
        // 对象列表 => 封装对象列表
        List<ProblemVO> problemVOList = problemList.stream().map(ProblemVO::objToVo).collect(Collectors.toList());
        problemVOPage.setRecords(problemVOList);
        return problemVOPage;
    }


    /**
     * 综合条件分页查询题目
     *
     * @param page                分页参数
     * @param problemQueryRequest 查询条件
     * @return 分页题目列表
     */
    @Override
    public Page<Problem> searchProblemPage(Page<Problem> page, ProblemQueryRequest problemQueryRequest) {
        if (problemQueryRequest == null) {
            return this.page(page);
        }

        // 调用Mapper的方法，直接在数据库层面进行所有条件的查询和分页
        return baseMapper.selectPageWithAll(page, problemQueryRequest);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateProblemTagsAndCases(Long problemId, List<Long> tagIdList, List<JudgeCaseAddRequest> judgeCaseList) {
        // 1. 更新标签
        if (tagIdList != null) {
            // 先删除原有的标签关联
            LambdaQueryWrapper<ProblemTag> tagQueryWrapper = new LambdaQueryWrapper<>();
            tagQueryWrapper.eq(ProblemTag::getProblemId, problemId);
            problemTagService.remove(tagQueryWrapper);

            // 如果标签列表不为空，则添加新的标签关联
            if (!tagIdList.isEmpty()) {
                List<ProblemTag> problemTagList = tagIdList.stream()
                        .map(tagId -> {
                            ProblemTag problemTag = new ProblemTag();
                            problemTag.setProblemId(problemId);
                            problemTag.setTagId(tagId);
                            return problemTag;
                        })
                        .collect(Collectors.toList());
                problemTagService.saveBatch(problemTagList);
            }
            // 如果标签列表为空，则只执行删除操作，不添加新标签
        }

        // 2. 更新用例
        if (judgeCaseList != null) {
            // 先删除原有的用例
            LambdaQueryWrapper<ProblemJudgeCase> caseQueryWrapper = new LambdaQueryWrapper<>();
            caseQueryWrapper.eq(ProblemJudgeCase::getProblemId, problemId);
            judgeCaseService.remove(caseQueryWrapper);

            // 如果用例列表不为空，则添加新的用例
            if (!judgeCaseList.isEmpty()) {
                judgeCaseService.saveBatch(judgeCaseList, problemId);
            }
            // 如果用例列表为空，则只执行删除操作，不添加新用例
        }
    }

    @Override
    public boolean updateProblemStatus(Long id, ProblemStatusEnum problemStatusEnum) {
        // 1 校验题目在不在
        if (id == null || problemStatusEnum == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Problem problem = getById(id);
        if (problem == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }
        switch (problemStatusEnum) {
            case PUBLISHED -> {
                // 3 判断题目是否已经发布
                if (ProblemStatusEnum.PUBLISHED.getValue().equals(problem.getStatus())) {
                    throw new BusinessException(ErrorCode.OPERATION_ERROR, "题目已发布");
                }
                // 2 判断题目是否存在测试用例
                long caseCount = judgeCaseService.count(new LambdaQueryWrapper<ProblemJudgeCase>().eq(ProblemJudgeCase::getProblemId, id));
                if (caseCount == 0) {
                    throw new BusinessException(ErrorCode.OPERATION_ERROR, "题目未添加测试用例");
                }
                return update(new LambdaUpdateWrapper<Problem>()
                        .eq(Problem::getId, id)
                        .set(Problem::getStatus, problemStatusEnum.getValue())
                        .set(Problem::getUpdateTime, new Date()));
            }
            case UNPUBLISHED -> {
                // 2 判断题目是否已经发布
                if (ProblemStatusEnum.UNPUBLISHED.getValue().equals(problem.getStatus())) {
                    throw new BusinessException(ErrorCode.OPERATION_ERROR, "题目未发布");
                }
                return update(new LambdaUpdateWrapper<Problem>()
                        .eq(Problem::getId, id)
                        .set(Problem::getStatus, problemStatusEnum.getValue())
                        .set(Problem::getUpdateTime, new Date()));
            }
        }

        return false;
    }

    @Override
    public List<HotProblemVo> getHotProblems(List<Long> hotProblemIds) {
        if (CollUtil.isEmpty(hotProblemIds)) {
            return List.of();
        }
        return baseMapper.selectHotProblems(hotProblemIds);
    }
}
